from logging import getLogger

from nevow.athena import LiveElement, expose
from nevow.flat import flatten
from nevow.loaders import stan
from nevow import tags

from zope.component import getUtility

from meercat.mapreduce import interfaces



__all__ = ['MapperList','MapperTypeForm']

log = getLogger('meercat.server.nevow.elements.mappers')

def moduleNameToDirectory(name):
    import sys
    from os.path import dirname
    __import__(name)
    return dirname(sys.modules[name].__file__)

def moduleReplace(input, pattern=r'\$\{((\w+\.)*\w+)\}'):
    from re import sub
    return sub(pattern, lambda match: moduleNameToDirectory(match.group(1)),
               input)

class MapperList(LiveElement):
    jsClass = u'Meercat.Mapper.List'
    
    docFactory = stan(tags.div(render=tags.directive('liveElement'))[
        tags.ul(class_='MapperListActions actionList')[
            tags.li()[
                tags.a(href='#',
                       onclick="Nevow.Athena.Widget.get(this).addMapperElement(); return false;")['Add Mapper']],
            tags.li()[
                tags.a(href='#',
                       onclick="Nevow.Athena.Widget.get(this).refresh(); return false;")['Refresh List']]],
        tags.ul(class_='MapperList'),])
    
    def getRegistry(self):
        return getUtility(interfaces.IMapperRegistry)
    
    @expose
    def getMapperList(self):
        log.info('%s.getMapperList()' % (str(self),))
        
        return map(lambda mapper: {u'hash':hash(mapper),
                                   u'formats':map(lambda format: u'%s -> %s' % (format[0], format[1]),
                                                  mapper.getFormats()),
                                   u'type': unicode(type(mapper))},
                   self.getRegistry().listMappers())
    
    @expose
    def deleteMapper(self, _hash):
        log.info('%s.deleteMapper(_hash=%s)' % (str(self),_hash))
        _hash = int(_hash)
        registry = self.getRegistry()
        
        mappers = filter(lambda mapper: hash(mapper) == _hash,
                         registry.listMappers())
        
        map(lambda mapper: registry.unregister(mapper), mappers)
        
        return True
    
    @expose
    def getAddMapperElement(self):
        log.info('%s.getAddMapperElement()' % (str(self),))
        e = MapperTypeForm()
        e.setFragmentParent(self)
        
        return e

class MapperTypeForm(LiveElement):
    jsClass = u'Meercat.Mapper.Types'
    
    @property
    def types(self):
        return {'MARC21 to XML': MarcXmlMapperForm,
                'XSLT': XslMapperForm,}
    
    @property
    def docFactory(self):
        return stan(tags.div(class_='yui-g',
                             render=tags.directive('liveElement'))[
        tags.h1()['Add Mapper'],
        tags.a(href='#',onclick='Nevow.Athena.Widget.get(this).remove(); return false;')['Close'],
        tags.div(class_='mapperTypeForm')[
            tags.h2()['Select Mapper Type'],
                tags.form()[
                    tags.ul()[
                        tags.li()[tags.select(name='type',
                                              onchange="Nevow.Athena.Widget.get(this).selectType();")[
                            tags.option(value='')['Select Mapper Type'],
                            map(lambda x: tags.option(value=x)[x],
                                sorted(self.types.keys()))]]]]]])
    
    @expose
    def getMapperForm(self, type):
        log.info('%s.getMapperForm(type=%s)' % (str(self), type))
        if not type:
            return False
        
        if not type in self.types:
            raise ValueError('Unkown mapper type')
        
        form = self.types[type]()
        form.setFragmentParent(self)
        
        return form

class MapperForm(LiveElement):
    jsClass = u'Meercat.Mapper.Form'
    
    formatCount = 0
    
    @property
    def docFactory(self):
        return stan(tags.div(class_='mapperForm',
                             render=tags.directive('liveElement'))[
            tags.form()[
                tags.ul()[
                    self.getFields(),
                    tags.input(value="Add Mapper", type="button",
                               onclick="Nevow.Athena.Widget.get(this).submit();",)]]])
    
    def getFields(self):
        log.info('%s.getFields()' % (str(self),))
        return [tags.li(_class='mapperFormats')['Formats:',self.getFormatInput(),
                                                self.getFormatInput(),
                                                tags.a(href='#',onclick='Nevow.Athena.Widget.get(this).addFormatInput(); return false;')['Add another format']]]
    
    def getFormatInput(self):
        self.formatCount += 1
        
        return tags.div()[
            tags.input(type='text',name='formatSource%s' % (self.formatCount,)),
            tags.input(type='text',name='formatTarget%s' % (self.formatCount,))]
    
    @expose
    def getFlatFormatInput(self):
        return unicode(flatten(self.getFormatInput()))
    
    def cleanFormatInput(self, data):
        data['formats'] = []
        
        for i in range(1,self.formatCount+1):
            format = (data.pop('formatSource%s' % (i,),'').strip(),
                      data.pop('formatTarget%s' % (i,),'').strip(),)
            
            if format[0] and format[1]:
                data['formats'].append(format)
        
        return data
    
    @expose
    def submit(self, **data):
        log.info('%s.submit(%s)' % (str(self),str(data)))
        
        data = self.cleanFormatInput(data)
        
        return getUtility(interfaces.IMapperRegistry).register(self._createMapper(data))

class MarcXmlMapperForm(MapperForm):
    def _createMapper(self, data):
        from meercat.mapreduce.mappers.marc import Marc21ToMarcXmlMapper
        
        formats = None
        
        if 'formats' in data and len(data['formats']) > 0:
            formats = data['formats']
        
        return Marc21ToMarcXmlMapper(formats)

class XslMapperForm(MapperForm):
    def getFields(self):
        log.info('%s.getFields()' % (str(self),))
        fields = [tags.li()['Stylesheet: ',
        
                            tags.input(type='text',name='stylesheet')]]
        fields.extend(super(XslMapperForm, self).getFields())
        return fields
    
    def _createMapper(self, data):
        if not 'formats' in data or len(data['formats']) == 0:
            raise ValueError('Formats are required')
        
        data['stylesheet'] = moduleReplace(data['stylesheet'].strip())
        log.info('stylesheet: %s' % (str(data['stylesheet'])))
        from meercat.mapreduce.mappers.xsl import XslMapper
        
        return XslMapper(*map(lambda t: (t[0],t[1],data['stylesheet']),
                              data['formats']))
